package com.nulldev.util.internal.backport.httpclient_rw.backports;

import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

import com.nulldev.util.internal.backport.concurrency9.Sets;

public class BackportedCollectors {
	static final Set<Collector.Characteristics> CH_UNORDERED_NOID = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED));

	/**
	 * Returns a {@code Collector} that accumulates the input elements into an
	 * <a href="../Set.html#unmodifiable">unmodifiable Set</a>. The returned
	 * Collector disallows null values and will throw {@code NullPointerException}
	 * if it is presented with a null value. If the input contains duplicate
	 * elements, an arbitrary element of the duplicates is preserved.
	 *
	 * <p>
	 * This is an {@link Collector.Characteristics#UNORDERED unordered} Collector.
	 *
	 * @param <T> the type of the input elements
	 * @return a {@code Collector} that accumulates the input elements into an
	 *         <a href="../Set.html#unmodifiable">unmodifiable Set</a>
	 * @since 10
	 */

	@SuppressWarnings("unchecked")
	public static <T> Collector<T, ?, Set<T>> toUnmodifiableSet() {
		return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add, (left, right) -> {
			if (left.size() < right.size()) {
				right.addAll(left);
				return right;
			} else {
				left.addAll(right);
				return left;
			}
		}, set -> (Set<T>) Sets.of(set.toArray()), CH_UNORDERED_NOID);
	}

	@SuppressWarnings("unchecked")
	private static <I, R> Function<I, R> castingIdentity() {
		return i -> (R) i;
	}

	/**
	 * Simple implementation class for {@code Collector}.
	 *
	 * @param <T> the type of elements to be collected
	 * @param <R> the type of the result
	 */
	static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
		private final Supplier<A> supplier;
		private final BiConsumer<A, T> accumulator;
		private final BinaryOperator<A> combiner;
		private final Function<A, R> finisher;
		private final Set<Characteristics> characteristics;

		CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Function<A, R> finisher,
				Set<Characteristics> characteristics) {
			this.supplier = supplier;
			this.accumulator = accumulator;
			this.combiner = combiner;
			this.finisher = finisher;
			this.characteristics = characteristics;
		}

		CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Set<Characteristics> characteristics) {
			this(supplier, accumulator, combiner, castingIdentity(), characteristics);
		}

		@Override
		public BiConsumer<A, T> accumulator() {
			return accumulator;
		}

		@Override
		public Supplier<A> supplier() {
			return supplier;
		}

		@Override
		public BinaryOperator<A> combiner() {
			return combiner;
		}

		@Override
		public Function<A, R> finisher() {
			return finisher;
		}

		@Override
		public Set<Characteristics> characteristics() {
			return characteristics;
		}
	}
}
